/*******************************************************************************
  * Copyright (c) 2013 E.W.Clay.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *******************************************************************************/ 

#pragma once

#include "render/surface.h"
#include "render/render.h"
#include "render/font.h"

namespace Imgui
{
	struct Window
	{
		Window() : x(0), y(0), width(0), height(0), offsetX(0), offsetY(0) {}
		Window(int x, int y, int width, int height) : x(x), y(y), width(width), height(height), offsetX(0), offsetY(0) {}
	
		int x, y, width, height;
		int offsetX, offsetY;
	};

	// Layouts can have a major axis (horizontal for horizontal list, vertical for vertical list or tree)
	// and a minor axis (or two minor axes in the case of a fixed window),
	// or neither major nor minor axes (table).
	enum LayoutFlags
	{
		// layout
		FIT				= 1,		// Fill all remaining space along the major axis.
		BACK			= (1<<1),	// Place at the back of the major axis.
		CENTRE			= (1<<2),	// Place at the centre of the minor axis.
		EXPAND			= (1<<3),	// Fill all space along the minor axis.
		INDENT			= (1<<4),	// Use the x coordinate of the window as an indentation amount.
		ALLOW_OVERFLOW	= (1<<5),	// Widgets are not clipped to the size of the window.
		USE_INPUT_XY	= (1<<6),	// Uses the x and y coordinates of the window to place at a fixed location.
		USE_CACHED_SIZE = (1<<8),	// When a window is provided, use only the x and y, taking width and height from the previous frame.
		NO_SEPARATION	= (1<<9),	// No separation of widgets within a layout.

		// graphical
		EMPTY			= (1<<10),	// Don't render the background.
		BORDERLESS		= (1<<11),	// Don't render borders.
		SELECTED		= (1<<12),	// Highlight as if selected.
		GHOSTED			= (1<<13),	// Disable and show ghosted.
		SHADOW			= (1<<14),	// Draw text with a shadow.
		TITLE			= (1<<15),	// Use the title style for a label.
		BUTTON			= (1<<16),	// Use the button style for a label.

		// action
		FORCE_FOCUS		= (1<<20),	// Prevent any other widget from gaining focus.
		ON_MOUSE_DOWN	= (1<<21),	// Trigger on mouse down (default is click and release).
		ON_MOUSE_UP		= (1<<22),	// Trigger on mouse up (default is click and release).
		R_MOUSE			= (1<<23),	// Use the right mouse button instead of the left.
		FLOAT			= (1<<24),	// Takes priority over any widgets not marked FLOAT.
		NO_OFFSET		= (1<<25),	// For drag box, don't take into account the relative position of the click.
		REPEAT			= (1<<26),	// Repeat the counter of the previous widget so that the new one will not hide it.
	};

	struct LayoutStyle
	{
		// rgba
		uint32 layoutBackground;
		uint32 layoutHighlight;
		uint32 layoutShadow;
		uint32 buttonNormal;
		uint32 titleBackground;
		uint32 buttonPressed;
		uint32 buttonSelected;
		uint32 buttonText;
		uint32 buttonTextShadow;
		uint32 buttonTextGhosted;
		uint32 textBackground;

		int margin;
		int indentSize;

		LayoutStyle() : m_pFont(0) {SetDefaults();}
		~LayoutStyle();

		void SetDefaults();
		void SetFont(Render::Font* pFont);
		const Render::Font* GetFont() const {return m_pFont;}

	private:
		Render::Font* m_pFont;
	};

	class Ui
	{
	public:
		// Call Initialise() before the first use of the UI.
		// Call Shutdown() after the last use of the UI and before program exit.
		// Shutdown() will automatically be called by the destructor.
		void Initialise();
		void Shutdown();

		// Call OnEvent() for each keyboard event (other events will be ignored).
		void OnEvent(const App::Event* pEvent);

		// Widget functions.
		// Widgets return true when triggered, except for those returning an index, which return >= 0.
		// For flag definitions see above.
		// pWindow is in/out. Input usage depends on flags, output is the clipped window used for the widget.

		// Basic label.
		void DoLabel(const char* text, uint32 flags = 0);

		// Basic button.
		bool DoButton(const char* text, uint32 flags = 0, Window* pWindow = 0);

		// Text box.
		// text (in/out) = the initial content of the box and after trigger.
		bool DoTextBox(std::string& text, uint32 flags = 0, Window* pWindow = 0);
	
		// MenuStrip is horizonal, Menu is vertical.
		// count = number of options.
		// window (out) = the position of the menu under the selected option.
		// DoMenuStrip returns the index of the selected option, or -1.
		// DoMenu returns the index of the selected option, or count for no selection, or -1.
		int DoMenuStrip(const char* options[], int count, Window& window, Window* pWindow = 0);
		int DoMenu(const char* options[], uint32 optionFlags[], int count, uint32 flags = 0, Window* pWindow = 0);

		// Helpers for text boxes with labels.
		// text / n (in/out) = the initial content of the box and after trigger.
		bool DoLabeledTextBox(const char* label, std::string& text, uint32 flags = 0, Window* pWindow = 0);
		bool DoLabeledIntBox(const char* label, int& n, uint32 flags = 0, Window* pWindow = 0);

		// Like a menu but without open/close behaviour.
		// rClick (out) = true on right click.
		int DoListBox(const char* items[], uint32 itemFlags[], int count, bool& rClick, uint32 flags, Window* pWindow = 0);
		
		// Drag box.
		// active (out) = true on mouse down.
		// Returns the new position of the window.
		bool DoDragBox(Window& window, bool& active, uint32 flags = 0);

		// Hot box.
		// Triggered on mouse over.
		// (out) details of mouse activity inside the window.
		bool DoHotBox(Window& window, int& mouseX, int& mouseY, bool& click, bool& release, uint32 flags = 0);
		bool DoHotBox(Window& window, int& mouseX, int& mouseY, bool& click, bool& release, bool& rClick, bool& rRelease, uint32 flags = 0);

		// Scroll bar (vertical).
		// offset (in/out) = amount in pixels to scroll the window.
		// proportion = scaling factor from moving bar size to scroll bar height.
		bool DoVerticalScrollBar(int& offset, float proportion, uint32 flags = 0);

		// Tree node.
		// mayExpand = whether the node is allowed to expand or not.
		// expanded (in/out) = whether the node is expanded or not.
		// rClick (out) = true on right click.
		bool DoTreeNode(const char* text, bool mayExpand, bool& expanded, bool& rClick, uint32 flags, Window* pWindow = 0);

		// Use to release a hot box before changing the UI.
		// (only required for hot box as all other widgets are released on trigger)
		void Release();

		// Force the internal counter to a given value.
		// This can be used to keep IDs synchronised when the UI changes.
		void SetCounter(int c);

		// Get the window currently in use for the current layout.
		const Window& GetVisibleWindow() const;

		// Get the full-size window for the current layout, before clipping.
		const Window& GetFullWindow() const;

		struct UiState;
		LayoutStyle& GetStyle();
		UiState& GetState();

	private:
		Ut::SharedPtr<UiState> m_pState;
	};

	// Base class for layouts.
	class Layout
	{
	public:
		~Layout();

	protected:
		Layout() {}
		Ui* pUi;
	};

	// To be used before any other layout or widget; begins and ends the frame.
	class Frame : public Layout
	{
	public:
		Frame(Ui& ui, Render::SurfacePtr pSurface, Window* pWindow = 0);
		~Frame();
	};

	// A window with fixed coordinates.
	// Use EXPAND or CENTRE to place items within this.
	class FixedWindow : public Layout
	{
	public:
		FixedWindow(Ui& ui, uint32 flags, const Window* pWindow);
	};

	// Horizontal list.
	// Default is to place items at the front of the list. Can also use FIT, BACK and/or EXPAND.
	class HorizontalList : public Layout
	{
	public:
		HorizontalList(Ui& ui, uint32 flags, Window* pWindow = 0);
	};

	// Vertical list.
	// Default is to place items at the front of the list. Can also use FIT, BACK and/or EXPAND.
	class VerticalList : public Layout
	{
	public:
		VerticalList(Ui& ui, uint32 flags, Window* pWindow = 0);
	};

	// A tree with expandable nodes.
	class Tree : public Layout
	{
	public:
		Tree(Ui& ui, uint32 flags, Window* pWindow = 0);
		~Tree();

		// Call around expanded nodes.
		void BeginBranch();
		void EndBranch();
	};

	// A two-dimensional table of rows. 
	class Table : public Layout
	{
	public:
		Table(Ui& ui, uint32 flags, Window* pWindow = 0);

		// Call around rows.
		void BeginRow();
		void EndRow();
	};
}
